﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;

// Inspired by Kent Boogaart, http://stackoverflow.com/questions/1083224/pushing-read-only-gui-properties-back-into-viewmodel
// this class gives access to the widths of the columns in a grid
// its Widths property is assumed to be empty and is filled when the grid is loaded. 


namespace Elca.MvvmHelpers {
    public static class ColumnSizeObserver {

        private static readonly Dictionary<ObservableCollection<double>, Grid> s_widthsMap = new Dictionary<ObservableCollection<double>, Grid>();

        public static readonly DependencyProperty IsWidthReadyProperty = DependencyProperty.RegisterAttached(
            "IsWidthReady",
            typeof(bool),
            typeof(ColumnSizeObserver));

        public static readonly DependencyProperty WidthsProperty = DependencyProperty.RegisterAttached(
            "Widths",
            typeof(ObservableCollection<double>),
            typeof(ColumnSizeObserver), 
            new FrameworkPropertyMetadata(OnWidthsChanged));

        public static bool GetIsWidthReady(FrameworkElement frameworkElement) {

            return (bool)frameworkElement.GetValue(IsWidthReadyProperty);
        }

        public static void SetIsWidthReady(FrameworkElement frameworkElement, bool value) {

            frameworkElement.SetValue(IsWidthReadyProperty, value);
        }

        public static ObservableCollection<double> GetWidths(FrameworkElement frameworkElement) {

            return (ObservableCollection<double>)frameworkElement.GetValue(WidthsProperty);
        }

        public static void SetWidths(FrameworkElement frameworkElement, ObservableCollection<double> observedWidths) {

            frameworkElement.SetValue(WidthsProperty, observedWidths);
        }

        private static void OnWidthsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) {

            var grid = (Grid) dependencyObject;
            grid.Loaded += OnGridLoaded;

            if (e.OldValue != null) {
                ObservableCollection<double> widths = (ObservableCollection<double>) e.OldValue;
                widths.CollectionChanged -= OnWidthCollectionChanged;
            }
        }

        private static void OnGridLoaded(object sender, RoutedEventArgs e) {
            var grid = (Grid) sender;
            grid.Loaded -= OnGridLoaded;

            ObservableCollection<double> widths = GetWidths(grid);

            FillWidths(grid, widths);

            s_widthsMap[widths] = grid;
            widths.CollectionChanged += OnWidthCollectionChanged;
            SetIsWidthReady(grid, true);
        }

        private static void OnWidthCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {

            if (e.Action == NotifyCollectionChangedAction.Replace) {

                ObservableCollection<double> widths = (ObservableCollection<double>) sender;
                Grid g = s_widthsMap[widths];
                g.ColumnDefinitions[e.NewStartingIndex].Width = new GridLength(widths[e.NewStartingIndex], GridUnitType.Pixel);
            }
        }

        private static void FillWidths(Grid grid, ObservableCollection<double> widths) {

            widths.Clear();
            
            foreach (ColumnDefinition cd in grid.ColumnDefinitions) {
                
                widths.Add(cd.ActualWidth);
            }
        }
    }
}
